﻿using KnBinarySerializer.Field;
using KnBinarySerializer.Ver;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.ConstrainedExecution;
using System.Text;
using System.Threading.Tasks;

namespace KnBinarySerializer
{
    internal class BianaySerializeManager:IDisposable
    {
        public const byte CLASS_FORMATTER_KEY = (byte)0;

        //Dimensional dimensionalTo;
        //Dimensional dimensionalFrom;
        bool isSameDimensional;
        FormatVer formatVer;
        Stream stream;
        FormaterPool formaterPool;
        /// <summary>
        /// 进行序列化时使用。
        /// </summary>
        /// <param name="pDimensional"></param>
        public BianaySerializeManager(FormatVer ver = FormatVer.Lastest, Dimensional dimensionalSource = Dimensional.Small, Dimensional dimensionalTarget = Dimensional.Small)
        {
            formatVer = ver;
            isSameDimensional = (dimensionalTarget == dimensionalSource);
            stream = new MemoryStream(10240);
            ClassSerialContext = new ClassSerialContext();
            initStartBytes(ver, dimensionalTarget);
        }
        //在开始保留一下位置记录类型序列化的位置
        long classSerialFlot = -1;
        private void initStartBytes(FormatVer ver, Dimensional dimensionalTarget = Dimensional.Small) { 
            stream.WriteByte((byte)dimensionalTarget);
            if (ver != FormatVer.Ver1)
            {
                writeVerion();
                //classSerialFlot = stream.Position;//记录类型序列化的位置。预留一个位置
                //Write(classSerialFlot);//该值无意义，刚好是一个位置信息而已。在获取结果前。会在结果尾部添加类型字段描述时会回填这个位置
                ClassSerialContext.WriteFlot(this);
            }
            formaterPool = FormaterPool.GetFormaterPool(ver);            
        }
        /// <summary>
        /// 反序列化时使用
        /// </summary>
        /// <param name="buffer"></param>
        public BianaySerializeManager(byte[] buffer, Dimensional dimensionalTarget = Dimensional.Small) : this(
            new MemoryStream(buffer),dimensionalTarget)
        {

        }
        public BianaySerializeManager(Stream pStream, Dimensional dimensionalTarget = Dimensional.Small)
        {
            ClassSerialContext = new ClassSerialContext();
            stream =pStream;
            isSameDimensional = true;//默认相同
            //读取源码,有内容时应为反序列化
            if (stream.Length > 0)
            {
                int di = stream.ReadByte();
                isSameDimensional = (di == (int)dimensionalTarget);
                FormatVer ver;
                if (!readVersion(stream, out ver))
                    throw new KnBinaryException(0, "不支持的数据格式");
                formatVer = ver;
                formaterPool = FormaterPool.GetFormaterPool(ver);
                if (formatVer != FormatVer.Ver1)
                    ClassSerialContext.ReadSerial(this);
            }
            else
            {
                //无内容时为准备序列化
                initStartBytes(FormatVer.Lastest, dimensionalTarget);
            }
        }

        public long GetPosition()
        {
            return stream.Position;
        }
        public void SetPosition(long position)
        {
            stream.Position = position;
        }
        public bool IsEnd()
        {
            return (stream.Length - stream.Position <= 0);
        }
        public int GetLength(long start)
        {
            return (int)(this.GetPosition() - start);
        }

        public bool Skip(int len)
        {
            long nextPos = this.stream.Position + len;
            if (nextPos < this.stream.Length)
            {
                this.stream.Position = nextPos;
                return true;
            }
            return false;
        }
        #region 读取部分

        public int GetBytesLen(Type type)
        {
            switch (type.Name)
            {
                case "Byte":
                case "Boolean":
                    return 1;
                case "Char":
                case "Short":
                    return 2;
                case "Int32":
                case "Decimal":
                    return 4;
                case "Double":
                case "Int64":
                case "Long":
                    return 8;
                default:
                    return -1;
            }
        }
        public byte ReadByte()
        {
            return (byte)stream.ReadByte();
        }
        public byte[] ReadBytes(int len)
        {
            byte[] buffer = new byte[len];
            stream.Read(buffer, 0, buffer.Length);
            return buffer;
        }

        public short ReadShort()
        {
            byte[] buffer = ReadBytes(2);
            if (!isSameDimensional)
            {
                Array.Reverse(buffer);
            }
            return BitConverter.ToInt16(buffer, 0);
        }
        /// <summary>
        /// 读4位
        /// </summary>
        /// <returns></returns>
        public int ReadInt()
        {
            byte[] buffer = ReadBytes(4);
            if (!isSameDimensional)
            {
                Array.Reverse(buffer);
            }
            return BitConverter.ToInt32(buffer, 0);
        }
        
        public long ReadLong()
        {
            byte[] buffer = ReadBytes(8);
            if (!isSameDimensional)
            {
                Array.Reverse(buffer);
            }
            return BitConverter.ToInt64(buffer, 0);
        }
        public double ReadDouble()
        {
            byte[] buffer = ReadBytes(8);
            if (!isSameDimensional)
            {
                Array.Reverse(buffer);
            }
            return BitConverter.ToDouble(buffer, 0);
        }
        public decimal ReadDecimal()
        {
            int[] buffer = new int[4];
            for(int i=0; i < buffer.Length; i++)
            {
                buffer[i] = ReadInt();
            }
            
            return new Decimal(buffer);
        }
        public char ReadChar()
        {
            byte[] buffer = ReadBytes(2);
            if (!isSameDimensional)
            {
                Array.Reverse(buffer);
            }
            return BitConverter.ToChar(buffer, 0);
        }
        public int Write(char value)
        {
            byte[] buffter = BitConverter.GetBytes(value);
            if (!isSameDimensional)
            {
                Array.Reverse(buffter);
            }
            this.Write(buffter, 0, buffter.Length);
            return buffter.Length;
        }
        #endregion
        public byte[] GetSerialResult()
        {
            if(formatVer!= FormatVer.Ver1)
            {
                //写入类型序列                
                ClassSerialContext.WriteSerial(this);
            }
            MemoryStream memory = stream as MemoryStream;
            if (memory != null)
                return memory.ToArray();
            else
                return null;
        }
        public T DeSerializeObject<T>()
        {
            return (T)DeSerializeObject(typeof(T));
        }
        public void DeSerializeToObject(object target)
        {
            int index = 0;
            byte key = this.ReadByte();
            IFormatter formatter = GetFormatter(key);
            if (formatter == null)
                throw new Exception("不支持序列化类型,key：" + key);
            int len = -1;
            if (!formatter.IsFixLen())
            {
                len = this.ReadInt();
                index += 5;
            }
            //byte[] subContent = new byte[len];
            //Array.Copy(content, index, subContent, 0, len);
            IItemFormatter itemFormatter = formatter as IItemFormatter;
            if (itemFormatter == null)
                throw new Exception("不支持Item序列化,key:" + key);
            itemFormatter.ReadBytes(this, len, target);
        }
        public object DeSerializeObject(Type type)
        {
            int index = 0;
            byte key = this.ReadByte();            
            IFormatter formatter = GetFormatter(key);
            if (formatter == null)
                throw new Exception("不支持序列化类型,key：" + key);
            int len = -1;
            if (!formatter.IsFixLen())
            {
                len = this.ReadInt();
                index += 5;
            }
            //byte[] subContent = new byte[len];
            //Array.Copy(content, index, subContent, 0, len);            
            return formatter.ReadBytes(this,len, type);
        }

        public int WriteNull(IFormatter formatter=null)
        {
            byte key = 0;//默认使用ClassFormatter的key
            if(formatter!=null)
                key = formatter.GetKey();
            this.Write(key);//写入序列器记号  
            this.Write((int)0);
            return 5;
        }

        //public int WriteDBNull()
        //{
        //    //取DBNullFormatter
        //    this.Write(key);//写入序列器记号  
        //    this.Write((int)0);
        //    return 5;
        //}

        public void WriteLenAndBytes(Func<BianaySerializeManager, int> writeFunc)
        {
            //写入一个临时的长度占用
            long postLen = stream.Position;
            this.Write(new byte[4]);
            //写入内容
            int lenMark = writeFunc(this);
            long postLast = stream.Position;
            //回填长度            
            byte[] lenBuffer = this.GetBytes(lenMark);
            stream.Position = postLen;
            stream.Write(lenBuffer, 0, lenBuffer.Length);
            stream.Position = postLast;
        }

        /// <summary>
        /// 通过值序列化对象。如果graph为null，将无法获取其处理器，所有本方法不作任何处理，直接返回0
        /// </summary>
        /// <param name="graph"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public int SerializeObject(object graph)
        {
            if (graph == null)
                return 0;
            //写下开始位置，以计算总长度
            long start = stream.Position;
            IFormatter formatter = GetFormatter(graph);
            if (formatter == null)
                throw new Exception("不支持序列化类型：" + graph.GetType().FullName);
            byte key = formatter.GetKey();            
            this.Write(key);//写入序列器记号            
            //写入一个临时的长度占用
            long postLen = stream.Position; 
            if(!formatter.IsFixLen())
                this.Write(new byte[4]);
            //写入内容
            int lenMark = formatter.WriteBytes(this, graph);
            if (!formatter.IsFixLen())
            {                
                long postLast = stream.Position;
                //回填长度            
                byte[] lenBuffer = this.GetBytes(lenMark);
                stream.Position = postLen;
                stream.Write(lenBuffer, 0, lenBuffer.Length);
                stream.Position = postLast;               
            }
            //计算位置差，返回总写入长度
            return this.GetLength(start);
        }
        public byte[] GetBytes(int value)
        {
            byte[] buffter = BitConverter.GetBytes(value);
            if (!isSameDimensional)
            {
                Array.Reverse(buffter);
            }            
            return buffter;
        }
        public int Write(int value)
        {
            byte[] buffter = BitConverter.GetBytes(value);
            if (!isSameDimensional)
            {
                Array.Reverse(buffter);
            }
            this.Write(buffter, 0, buffter.Length);
            return buffter.Length;
        }

        public int Write(float value)
        {
            byte[] buffter = BitConverter.GetBytes(value);
            if (!isSameDimensional)
            {
                Array.Reverse(buffter);
            }
            this.Write(buffter, 0, buffter.Length);
            return buffter.Length;
        }
        public float ReadFloat()
        {
            byte[] buffer = ReadBytes(4);
            if (!isSameDimensional)
            {
                Array.Reverse(buffer);
            }
            return BitConverter.ToSingle(buffer, 0);
        }

        public int Write(short value)
        {
            byte[] buffter = BitConverter.GetBytes(value);
            if (!isSameDimensional)
            {
                Array.Reverse(buffter);
            }
            this.Write(buffter, 0, buffter.Length);
            return buffter.Length;
        }

        public int Write(double value)
        {
            byte[] buffter = BitConverter.GetBytes(value);
            if (!isSameDimensional)
            {
                Array.Reverse(buffter);
            }
            this.Write(buffter, 0, buffter.Length);
            return buffter.Length;
        }
        public int Write(long value)
        {
            byte[] buffter = BitConverter.GetBytes(value);
            if (!isSameDimensional)
            {
                Array.Reverse(buffter);
            }
            this.Write(buffter, 0, buffter.Length);            
            return buffter.Length;
        }


        public int Write(decimal value)
        {
            int[] buffter = decimal.GetBits(value);
            int total = 0;
            for (int i = 0; i < buffter.Length; i++)
                total += Write(buffter[i]);
            return total;
        }

        public void Write(byte[] buffer)
        {
            this.Write(buffer, 0, buffer.Length);
        }
        public void Write(byte byt)
        {
            stream.WriteByte(byt);
        }

        public void Write(byte[] buffer, int index, int len)
        {
            stream.Write(buffer, index, len);
        }

        public void Dispose()
        {
            stream?.Dispose();
        }
        /// <summary>
        /// 类型序列。收集类的字段，统一写入到结果尾部
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public int WriteClassSerial(Type type,out ClassSerial typeSerial,out Dictionary<string, IClassField> fields)
        {
            (typeSerial,fields) = ClassSerialContext.GetClassSerial(type);
            //只需写入序号位置。类型的typeSerial，主要是字段名及先后顺序，会在整个结束前写入尾部
            return Write(typeSerial.SerialNo);
        }

        public ClassSerialContext ClassSerialContext { get; set; }
        public ClassSerial ReadClassSerial()
        {
            short serialNo = ReadShort();
            //序列化前要读取尾部的TypeSerial
            return ClassSerialContext.GetClassSerial(serialNo);//通过该序列取回属性的次序
        }       
        public IFormatter GetFormatter(object obj)
        {
            return formaterPool.GetFormatter(obj);
        }
        public IFormatter GetFormatter(Type type)
        {
            return formaterPool.GetFormatter(type);
        }
        public IFormatter GetFormatter(byte key)
        {
            return formaterPool.GetFormatter(key);
        }

        private void writeVerion()
        {            
            stream.WriteByte((byte)'K');
            stream.WriteByte((byte)'n');
            stream.WriteByte((byte)'B');//1=A,2=B
        }
        private bool readVersion(Stream stream,out FormatVer ver)
        {
            ver = FormatVer.Lastest;
            byte[] buffer = new byte[3];
            stream.Read(buffer, 0, buffer.Length);
            if (buffer[0] == 'K' && buffer[1] == 'n')
            {
                //版本号 = buffer[2] - 31;
                if (buffer[2] == 'B')//当前B为最新版本号
                    return true;
                return false;
            }
            else
            {
                stream.Position -= 3;
                ver = FormatVer.Ver1;
                return true;
            }
        }         
    }
}
